/**
 * 
 */
package org.vsg.stock.extension.processor;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.vsg.stock.basic.domain.StockDailyLine;
import org.vsg.stock.extension.processor.handler.StandardProcessorHandler;
import org.vsg.stock.extension.vo.AnalysisKLineCyclePatternVO;
import org.vsg.stock.extension.vo.AnalysisKLineUnitsVO;
import org.vsg.stock.extension.vo.ExtStockDailyLineVO;
import org.vsg.stock.extension.vo.HistoryPatternStock;

/**
 * @author vison
 *
 */
public class DailyKLineModePatternProcessor extends
		AbstractPatternFilterProcessor {
	
	private DecimalFormat decFormat = new DecimalFormat("##0.000");
	
	private List<StandardProcessorHandler> allStandardProcessorHandler;

	public List<StandardProcessorHandler> getAllStandardProcessorHandler() {
		return allStandardProcessorHandler;
	}

	public void setAllStandardProcessorHandler(
			List<StandardProcessorHandler> allStandardProcessorHandler) {
		this.allStandardProcessorHandler = allStandardProcessorHandler;
	}

	/* (non-Javadoc)
	 * @see org.vsg.stock.custcomp.processor.PatternFilterProcessor#calculateCloseValue(java.util.List, java.util.List)
	 */
	@Override
	public double calculateCloseValue(List<StockDailyLine> allDailyLine,
			List<HistoryPatternStock> hisPatternStockList) {
		// TODO Auto-generated method stub
		List<ExtStockDailyLineVO> extStkDailLineList = KLineHelperManager.convertExtStkDailyLineVOList( allDailyLine);
		List<AnalysisKLineUnitsVO> analyKLineUnitVOList = KLineHelperManager.detachAnalysisKLineUnits( extStkDailLineList );
		
		// --- change daily pattern ---
		List<AnalysisKLineCyclePatternVO> currentStockPatternVoList =  analyzedKLineUnits(analyKLineUnitVOList);
		
		HistoryPatternStock hisPatternStock = hisPatternStockList.get(0);
		List<StockDailyLine> otherStockDailyLine = this.getStockDailyLineDao().findStockLineByCycle( hisPatternStock.getCode().toUpperCase() , hisPatternStock.getStartDate(), allDailyLine.size());
		List<ExtStockDailyLineVO> extOtherStkDailLineList = KLineHelperManager.convertExtStkDailyLineVOList( otherStockDailyLine );
		List<AnalysisKLineUnitsVO> analyOtherKLineUnitVOList = KLineHelperManager.detachAnalysisKLineUnits( extOtherStkDailLineList );
		List<AnalysisKLineCyclePatternVO> otherStockPatternVoList =  analyzedKLineUnits( analyOtherKLineUnitVOList );		
		
		// --- current stock pattern vo list ---
		Collections.reverse( currentStockPatternVoList );
		Collections.reverse( otherStockPatternVoList );
		
		// ---- define the k line unit value ----
		AnalysisKLineCyclePatternVO akcpvo = null;
		List<Double> allfitValue = new Vector<Double>();
		int markIndex = 0;
		for (Iterator<AnalysisKLineCyclePatternVO> currentStockPatternVOIter = currentStockPatternVoList.iterator() ; currentStockPatternVOIter.hasNext() ;   ) {
			akcpvo = currentStockPatternVOIter.next();
			// --- run the other stock pattern vo list ---
			AnalysisKLineCyclePatternVO otherspvo = null;
			double tmpMaxValue = 100;

			for (int i = markIndex ; i < otherStockPatternVoList.size() ; i++) {
				otherspvo = otherStockPatternVoList.get(i);
				
				// compute value
				double upShadowRadio = akcpvo.getUpShadowRatio() - otherspvo.getUpShadowRatio();
				double upEntityRadio = akcpvo.getUpEntityRatio() - otherspvo.getUpEntityRatio();
				double downEntityRadio = akcpvo.getDownEntityRatio() - otherspvo.getDownEntityRatio();
				double downShadowRadio = akcpvo.getDownShadowRatio() - otherspvo.getDownShadowRatio();
				
				double directionRadio = Math.abs( akcpvo.getCycleDirection() - otherspvo.getCycleDirection() );
				double allCountRadio = Math.abs( upShadowRadio ) + Math.abs( upEntityRadio ) + Math.abs( downEntityRadio ) + Math.abs( downShadowRadio );
				// --- count the value, max direction value is  --
				double totalCount = directionRadio/20* 5 * 0.3 + allCountRadio * 0.7;
				if (totalCount < tmpMaxValue) {
					tmpMaxValue = totalCount;
					markIndex = i;
				}
			}
			double resultValue = Double.parseDouble( decFormat.format( 100 - tmpMaxValue ) );
			allfitValue.add( resultValue );
		}
		
		double subtotal = 0;
		for (int i = 0 ; i < allfitValue.size() ; i++) {
			subtotal+=allfitValue.get(i);
		}
		double resultValue =  Double.parseDouble( decFormat.format(subtotal/allfitValue.size() ));
		if (resultValue > 70) {
			return resultValue;
		}
		return 0;
	}
	
	private List<AnalysisKLineCyclePatternVO> analyzedKLineUnits(List<AnalysisKLineUnitsVO> analyKLineUnitVOList) {
		AnalysisKLineUnitsVO akuvo  = null;
		List<ExtStockDailyLineVO> esdlvoList = null;
		
		List<AnalysisKLineCyclePatternVO> analyKLineCyclePatternVOList = new ArrayList<AnalysisKLineCyclePatternVO>();
		
		ExtStockDailyLineVO extStkDailyLineVO = null;
		for (Iterator<AnalysisKLineUnitsVO> analysisIter = analyKLineUnitVOList.iterator() ; analysisIter.hasNext();) {
			akuvo = analysisIter.next();
			esdlvoList = akuvo.getExtStkDailLineVOList();
			int cycle = esdlvoList.size();
			if (cycle > 0 ) {
				extStkDailyLineVO = esdlvoList.get(0);
				double entityPriceHigh = extStkDailyLineVO.getOpen() > extStkDailyLineVO.getClose() ? extStkDailyLineVO.getOpen() : extStkDailyLineVO.getClose();
				double entityPriceLow = extStkDailyLineVO.getOpen() > extStkDailyLineVO.getClose() ? extStkDailyLineVO.getClose() : extStkDailyLineVO.getOpen();
				double halfEntityPrice = Double.parseDouble(  decFormat.format( entityPriceLow + Math.abs( extStkDailyLineVO.getEntity() ) / 2 ) );
				int upShadowCount = 0;
				int upEntityCount = 0;
				int downEntityCount = 0;
				int downShadowCount = 0;
				ExtStockDailyLineVO otherStkDailyLineVO = null;
				for (int i = 1 ; i < esdlvoList.size() ; i++) {
					otherStkDailyLineVO = esdlvoList.get(i);
					double hEntityPrice = Double.parseDouble(  decFormat.format( otherStkDailyLineVO.getOpen() > otherStkDailyLineVO.getClose() ? otherStkDailyLineVO.getClose() : otherStkDailyLineVO.getOpen() + Math.abs( otherStkDailyLineVO.getEntity() ) / 2 ) );
					if (hEntityPrice > entityPriceHigh) {
						++upShadowCount;
					} else if (hEntityPrice <= entityPriceHigh && hEntityPrice > halfEntityPrice) {
						++upEntityCount;
					} else if ( hEntityPrice <= halfEntityPrice && hEntityPrice >= entityPriceLow ) {
						++downEntityCount;
					} else {
						++downShadowCount;
					}
				}
				double upShadowRatio = Double.parseDouble( decFormat.format( (double)upShadowCount / cycle * 100) );
				double upEntityRatio = Double.parseDouble( decFormat.format( (double)upEntityCount / cycle  * 100) );
				double downEntityRatio = Double.parseDouble( decFormat.format( (double)downEntityCount / cycle * 100) );
				double downShadowRatio = Double.parseDouble( decFormat.format( (double)downShadowCount / cycle * 100) );
				
				// --- cycle direction , compute the last object defined ---
				// --- build pattern list object ---
				AnalysisKLineCyclePatternVO analKLineCyclePatternVo = new AnalysisKLineCyclePatternVO();
				analKLineCyclePatternVo.setStockCode( akuvo.getExtStkDailLineVOList().get(0).getStockCode() );
				analKLineCyclePatternVo.setCycle( esdlvoList.size() );
				analKLineCyclePatternVo.setStartDate( akuvo.getStartDate() );
				analKLineCyclePatternVo.setEndDate( esdlvoList.get( esdlvoList.size() - 1 ).getDate() );
				analKLineCyclePatternVo.setUpShadowRatio( upShadowRatio );
				analKLineCyclePatternVo.setUpEntityRatio( upEntityRatio );
				analKLineCyclePatternVo.setDownShadowRatio( downShadowRatio );
				analKLineCyclePatternVo.setDownEntityRatio( downEntityRatio );
				
				double standardPrice = extStkDailyLineVO.getClose();
				double firstHEntityPrice = 0;
				double lastHEntityPrice = 0;
				if (esdlvoList.size() > 2) {
					ExtStockDailyLineVO firstPoint = esdlvoList.get(1);
					ExtStockDailyLineVO lastPoint = esdlvoList.get(esdlvoList.size() - 1);
					
					firstHEntityPrice = Double.parseDouble(  decFormat.format( firstPoint.getOpen() > firstPoint.getClose() ? firstPoint.getClose() : firstPoint.getOpen() + Math.abs( firstPoint.getEntity() ) / 2 ) );
					lastHEntityPrice = Double.parseDouble(  decFormat.format( lastPoint.getOpen() > lastPoint.getClose() ? lastPoint.getClose() : lastPoint.getOpen() + Math.abs( lastPoint.getEntity() ) / 2 ) );

				} else {
					
					ExtStockDailyLineVO singlePoint = esdlvoList.get(esdlvoList.size() - 1);
					lastHEntityPrice =  singlePoint.getClose();
					firstHEntityPrice = singlePoint.getOpen();

				}
				double changeRatio = lastHEntityPrice - firstHEntityPrice;				
				double cycleDirection = Double.parseDouble( decFormat.format(changeRatio / standardPrice * 100 ) );
				analKLineCyclePatternVo.setCycleDirection( cycleDirection );				

				if (firstHEntityPrice > entityPriceHigh) {
					analKLineCyclePatternVo.setFirstPointState( AnalysisKLineCyclePatternVO.OVER_UPPER_ENTITY );							
				} else if (firstHEntityPrice <= entityPriceHigh && firstHEntityPrice > halfEntityPrice) {
					analKLineCyclePatternVo.setFirstPointState( AnalysisKLineCyclePatternVO.IN_UPPER_ENTITY );
				} else if ( firstHEntityPrice <= halfEntityPrice && firstHEntityPrice >= entityPriceLow ) {
					analKLineCyclePatternVo.setFirstPointState( AnalysisKLineCyclePatternVO.IN_LOWER_ENTITY );
				} else {
					analKLineCyclePatternVo.setFirstPointState( AnalysisKLineCyclePatternVO.UNDER_LOWER_ENTITY );
				}
				if (lastHEntityPrice > entityPriceHigh) {
					analKLineCyclePatternVo.setLastPointState( AnalysisKLineCyclePatternVO.OVER_UPPER_ENTITY );							
				} else if (lastHEntityPrice <= entityPriceHigh && lastHEntityPrice > halfEntityPrice) {
					analKLineCyclePatternVo.setLastPointState( AnalysisKLineCyclePatternVO.IN_UPPER_ENTITY );
				} else if ( lastHEntityPrice <= halfEntityPrice && lastHEntityPrice >= entityPriceLow ) {
					analKLineCyclePatternVo.setLastPointState( AnalysisKLineCyclePatternVO.IN_LOWER_ENTITY );
				} else {
					analKLineCyclePatternVo.setLastPointState( AnalysisKLineCyclePatternVO.UNDER_LOWER_ENTITY );
				}
				
				
				if (allStandardProcessorHandler != null) {
					StandardProcessorHandler sph = null;
					for (Iterator<StandardProcessorHandler> allStandProcHandlerIter = allStandardProcessorHandler.iterator() ; allStandProcHandlerIter.hasNext(); ) {
						sph = allStandProcHandlerIter.next();
						if (sph.checkStandardProcessorModule( esdlvoList)) {
							analKLineCyclePatternVo.addStandardProcessorModuleKey( sph.getProcessorHandlerCode() );
						}
					}
				}
				
				// --- find the standard pattern defined ---
				analyKLineCyclePatternVOList.add( analKLineCyclePatternVo );
	
			}
			
		}
		
		return analyKLineCyclePatternVOList;
	}
	

}
